#!/usr/bin/env python
from graph.GenGraph import GenGraph
from graph.Genedge import Genedge
from ConfigParser import ConfigParser
import random

class Mutator:
    
    def __init__(self, params):
        self.params = params
    
    def mutate_startshape(self, grapho):
        """
        basically, flips the startshape to a diferent(not yet), randomly chosen node. 5% per graph
        """
        grapho.set_startshape(random.choice(grapho.nodes()))
        print "MUTATOR: mutated startshape"
    
    def mutate_add_edge(self, grapho, mnode):
        """
        adds a randomly generated edge to a randomly chosen instance in specified node.
        """
        destination = random.choice(grapho.nodes())
        edge = Genedge(self.generate_paylod())
        
        instance = random.choice(grapho[mnode].instance_list())
        grapho.add_edge(mnode, destination, edge, instance)
        print "MUTATOR: added edge"
    
    def mutate_remove_edge(self, grapho, mnode):
        """
        deletes a randomly chosen edge. 1% per node
        no distinction (yet) between edges. 
        1 - choose instance
        2 - choose edge
        3 - remove edge
        """
        multinode = grapho[mnode]
        instances = multinode.instance_list()
        instance = random.choice(instances)
        edge = random.choice(multinode[instance])
        grapho[mnode][instance].remove(edge)
        print "MUTATOR: removed edge"
    
    def mutate_remove_node(self, grapho):
        """
        deletes a randomly chosen instance of a randomly chosen node. 20% per graph
        removes whole node if only a single instance exists.
        """
        mnode = random.choice(grapho.nodes()) #choose node
        multinode = grapho[mnode]       
        instances = multinode.instance_list()   
        instance = random.choice(instances) #choose instance
        del multinode.instances[instance]   #delete the instance     
        
        if len(multinode.instance_list()) == 0: #if last instance
            for node in grapho.nodes_iter(): #go through all nodes in the graph               
                if mnode in grapho[node].all_neighbours():  #if condemned mnode is a neighbour of node                   
                    grapho[node].remove_edges(mnode) #have him remove all edges towards dead node                
            grapho.remove_node(mnode)   #kill the node
    
    print "MUTATOR: removed node"
    
    def mutate_duplicate_node(self, grapho):
        """
        duplicates a randomly chosen instance of a randomly chosen node
        """
        mnode = random.choice(grapho.nodes())
        multinode = grapho[mnode]
        instances = multinode.instance_list()
        instance = random.choice(instances)
        multinode.duplicate_instance(instance)
        
        print "MUTATOR: duplicated node"
    
    
    def mutate_change_param(self, edge):
        """
        changes a randomly chosen parameter of a given edge. 1% per edge
        """
        if len(edge[1]) == 0:
            return
        
        payload = edge[1]
        params = payload.params
        param = random.choice(params)
        params.remove(param)
        params.append(self.generate_param())
        
        print "MUTATOR: changed param: " + str(param)
    
    def mutate_remove_param(self, edge):
        """
        removes a randomly chosen parameter from a given edge. 1% per edge
        edge -> the index (or name) of the edge in the instance.
        """
        if len(edge[1]) == 0:
            return
        
        param = random.choice(edge[1].params)
        edge[1].params.remove(param)
        print "MUTATOR: removed param: " + str(param)
    
    def mutate_add_param(self, edge):
        """
        adds randomly generated parameter to the payload of a given edge
        """
        param_to_add = self.generate_param()
        edge[1].add_param(param_to_add)
        print "MUTATOR: added param: " + str(param_to_add)
    
    def generate_param(self):
        """
        chooses a random parameter from all possible params and generates a random float
        generated value is a random float (E [0.0 to 1.0[ ) multiplied by either 1, 10, 100 or 1000 randomly
        """
        return tuple((random.choice(self.params), str(random.random() * random.choice([1,10,100,1000]))))
    
    def generate_paylod(self):
        """
        generates a randomly sized (0 to 5) payload of parameters. combined with a destination, it makes an edge.
        """
        args = []
        n_args = random.randint(0,5)
        for n in range(n_args):
            args.append(self.generate_param())
        return args